#include <msp430x26x.h>
#include "../Hardware/PortDef.h"
#include "../Utilities/Delay.h"
#include "../Communication/SPI.h"
#include "Accel.h"

// Control command defines
#define CONVERT_X 0x00
#define CONVERT_Y 0x01
#define CONVERT_Z 0x02
#define CONFIG    0x04

// Config values
#define CONFIG_ENABLE 0x04

// Private functions
static void sendCommand(unsigned char command);
static int getValue(void);

/*
 * This is the initialization routine for the accelerometer.  We simply
 * enable it, send the config character (selects the register), delay
 * for 250ns then send the enable character, then delay disable, and 
 * delay for good measure.  The timings may be able to be adjusted based
 * on the data sheet, but this worked for us.
 */
void AccelInit(void)
{
	ENABLE_ACCEL();
	DELAY_250NS();
	sendCommand(CONFIG);
	DELAY_250NS();
	sendCommand(CONFIG_ENABLE);
	DELAY_250NS();
	DISABLE_ACCEL();
	DELAY_250NS();
}

/*
 * Get the x axis acceleration.  Enable the accelerometer, send the convert
 * x command, delay for a long time (50uS or so), get the value and disable
 * the accelerometer.  Then delay for good measure for 250ns.  Return the
 * received value shifted right 4 (the value is in the top 12 bits of the
 * received bytes).  These timings could be adjusted slightly according
 * to the data sheet, but they worked for us.
 */
int AccelGetX(void)
{
	unsigned int temp;
	
	ENABLE_ACCEL();
	sendCommand(CONVERT_X);
	DELAY_50US();
	temp = getValue();
	DISABLE_ACCEL();
	DELAY_250NS();
	
	return (temp >> 4);
}

/*
 * Get the y axis acceleration.  Enable the accelerometer, send the convert
 * y command, delay for a long time (50uS or so), get the value and disable
 * the accelerometer.  Then delay for good measure for 250ns.  Return the
 * received value shifted right 4 (the value is in the top 12 bits of the
 * received bytes).  These timings could be adjusted slightly according
 * to the data sheet, but they worked for us.
 */
int AccelGetY(void)
{
	unsigned int temp;
	
	ENABLE_ACCEL();
	sendCommand(CONVERT_Y);
	DELAY_50US();
	temp = getValue();
	DISABLE_ACCEL();
	DELAY_250NS();
	
	return (temp >> 4);
}

/*
 * Get the z axis acceleration.  Enable the accelerometer, send the convert
 * z command, delay for a long time (50uS or so), get the value and disable
 * the accelerometer.  Then delay for good measure for 250ns.  Return the
 * received value shifted right 4 (the value is in the top 12 bits of the
 * received bytes).  These timings could be adjusted slightly according
 * to the data sheet, but they worked for us.
 */
int AccelGetZ(void)
{
	unsigned int temp;
	
	ENABLE_ACCEL();
	sendCommand(CONVERT_Z);
	DELAY_50US();
	temp = getValue();
	DISABLE_ACCEL();
	DELAY_250NS();
	
	return (temp >> 4);
}

/*
 * Send an arbitrary character over SPI.  This is manually bitbanged SPI
 * according to the accelerometer datasheet specification.
 */
static void sendCommand(unsigned char command)
{
	unsigned char i;
	
	for (i = 8; i > 0; i--)
	{
		SCLK_LOW();
		if (command & 0x80) // MSB first
		{
			MOSI_HIGH();
		}
		else
		{
			MOSI_LOW();
		}
		DELAY_250NS();
		SCLK_HIGH();
		DELAY_250NS();
		command <<= 1; // Put next bit in MSB
	}
}

/*
 * Receive a character over SPI.  This is manually bitbanged SPI
 * according to the accelerometer datasheet specification.
 */
static int getValue(void)
{
	unsigned int temp;
	unsigned int i;
	
	// Initialize data to zero
	temp = 0x0000;
	
	MOSI_LOW(); // 0's are placeholders
	
	for (i = 0x8000; i > 0; i>>=1)
	{
		SCLK_LOW();
		DELAY_250NS();
		if (MISO)
		{
			temp |= i;
		}
		SCLK_HIGH();
		DELAY_250NS();
	}
	
	return temp;
}
